import Vue from 'vue'
import { createApp } from './main'
import ProgressBar from './widget/components/progressBar.vue'

const { app, router, store } = createApp()

//因为store.js同样也会被打包到服务器运行的server.bundle.js中，所以运行环境不一定是浏览器，
//这里需要对window做判断，防止报错，同时如果有window.__INITIAL_STATE__属性，
//说明服务器已经把所有初始化需要的异步数据都获取完成了，要对store中的状态做一个替换，保证统一。
if (typeof window !== 'undefined' && window.__INITIAL_STATE__) {
  store.replaceState(window.__INITIAL_STATE__)
}

// global progress bar
const bar = (Vue.prototype.$bar = new Vue(ProgressBar).$mount())
document.body.appendChild(bar.$el)

//全局进度条
Vue.mixin({
  beforeRouteUpdate(to, from, next) {
    const { asyncData } = this.$options
    if (asyncData) {
      asyncData({
        store: this.$store,
        route: to
      })
        .then(next)
        .catch(next)
    } else {
      next()
    }
  }
})

// 路由器必须要提前解析路由配置中的异步组件，才能正确地调用组件中可能存在的路由钩子
// 路由准备就绪时执行一次
router.onReady(() => {
  // 注册钩子，客户端每次路由切换都会执行一次
  router.beforeResolve((to, from, next) => {
    const matched = router.getMatchedComponents(to)
    const prevMatched = router.getMatchedComponents(from)

    // 我们只关心非预渲染的组件
    // 很有可能的场景是详情A跳转到详情B，需要调用asyncData获取数据
    // 所以我们对比它们，找出两个匹配列表的差异组件
    let diffed = false
    const activated = matched.filter((c, i) => {
      return diffed || (diffed = prevMatched[i] !== c)
    })
    const asyncDataHooks = activated.map(c => c.asyncData).filter(_ => _)
    if (!asyncDataHooks.length) {
      return next()
    }
    // 触发加载指示器
    bar.start()
    Promise.all(asyncDataHooks.map(hook => hook({ store, route: to })))
      .then(() => {
        // 停止加载指示器
        bar.finish()
        next()
      })
      .catch(next)
  })

  // 挂载到DOM 中
  app.$mount('#app')
})
